Piano notes book, powered by Astro and React.
Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 

123 linhas
3.2 KiB

  1. import createVerovioModule from 'verovio/wasm';
  2. import { VerovioToolkit } from 'verovio/esm';
  3. import { readFile, readdir } from 'node:fs/promises';
  4. import { JSDOM } from 'jsdom';
  5. import type {APIRoute, GetStaticPaths} from 'astro';
  6. // @ts-ignore
  7. import tailwindConfig from '../../../tailwind.config.mjs';
  8. const filter = (musicXml: string) => {
  9. const jsdom = new JSDOM(musicXml, { pretendToBeVisual: true, contentType: 'image/svg+xml' });
  10. const win = jsdom.window;
  11. // const win = jsdom.window;
  12. return win.document.documentElement.outerHTML;
  13. };
  14. const processOutput = (xmlData: string) => {
  15. const jsdom = new JSDOM(xmlData, { pretendToBeVisual: true, contentType: 'image/svg+xml' });
  16. const win = jsdom.window;
  17. const [svgElemsRoot, svgElemsMain] = Array.from(win.document.getElementsByTagName('svg'));
  18. if (typeof svgElemsRoot === 'undefined') {
  19. return '';
  20. }
  21. if (typeof svgElemsMain === 'undefined') {
  22. return '';
  23. }
  24. // svgElemsRoot.getAttributeNames().forEach((a) => {
  25. // const attr = svgElemsRoot.getAttribute(a);
  26. // if (!attr) {
  27. // return;
  28. // }
  29. // svgElemsMain.setAttribute(a, attr);
  30. // });
  31. Array.from(svgElemsRoot.children).forEach((h) => {
  32. if (h !== svgElemsMain) {
  33. h.remove();
  34. if (h.tagName.toLowerCase() === 'desc') {
  35. return;
  36. }
  37. if (h.tagName.toLowerCase() === 'style') {
  38. h.innerHTML = h.innerHTML.replace(/Times,serif/g, tailwindConfig.theme.fontFamily.body.join(','));
  39. }
  40. if (svgElemsMain.children[0]) {
  41. svgElemsMain.insertBefore(h, svgElemsMain.children[0]);
  42. return;
  43. }
  44. svgElemsMain.appendChild(h);
  45. }
  46. });
  47. Array.from(win.document.getElementsByClassName('pgHead')).forEach(h => {
  48. h.remove();
  49. });
  50. Array.from(win.document.getElementsByClassName('pgFoot')).forEach(h => {
  51. h.remove();
  52. });
  53. Array.from(win.document.getElementsByTagName('defs')).forEach(h => {
  54. h.remove();
  55. if (svgElemsMain) {
  56. if (svgElemsMain.children[0]) {
  57. svgElemsMain.insertBefore(h, svgElemsMain.children[0]);
  58. } else {
  59. svgElemsMain.appendChild(h);
  60. }
  61. }
  62. });
  63. return `<?xml version="1.0" encoding="utf-8"?>${svgElemsMain.outerHTML}`
  64. };
  65. export const GET: APIRoute = async ({ params }) => {
  66. const verovioModule = await createVerovioModule();
  67. const score = await readFile(`public/scores/${params.asset}.musicxml`, 'utf-8');
  68. const verovioToolkit = new VerovioToolkit(verovioModule);
  69. const filteredScore = filter(score);
  70. const isSuccessful = verovioToolkit.loadData(filteredScore);
  71. if (!isSuccessful) {
  72. return new Response(null, { status: 500 });
  73. }
  74. verovioToolkit.setOptions({
  75. breaks: 'none',
  76. font: 'Bravura',
  77. });
  78. let data: string;
  79. try {
  80. const raw = verovioToolkit.renderToSVG(1)
  81. .replace(/xmlns:mei="(.+?)"/g, '')
  82. .replace(/xlink:/g, '');
  83. data = processOutput(raw);
  84. } catch (err) {
  85. console.error(err);
  86. return new Response(null, { status: 500 });
  87. }
  88. return new Response(
  89. data,
  90. {
  91. headers: {
  92. 'Content-Type': 'image/svg+xml',
  93. },
  94. status: 200,
  95. }
  96. );
  97. };
  98. export const getStaticPaths: GetStaticPaths = async () => {
  99. const files = await readdir('public/scores');
  100. return files
  101. .filter((f) => f.endsWith('.musicxml'))
  102. .map((f) => ({
  103. params: {
  104. asset: f.replace(/\.musicxml/g, ''),
  105. },
  106. }));
  107. };